package org.jsets.fastboot.persistent.interceptor;

import java.io.StringReader;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Objects;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.jsets.fastboot.persistent.IDataScopeService;
import org.jsets.fastboot.persistent.config.PersistentAnnotationResolver;
import org.jsets.fastboot.persistent.impl.JaversService;
import org.jsets.fastboot.persistent.metadata.DataScopeOption;
import org.jsets.fastboot.persistent.metadata.PersistentOption;
import org.jsets.fastboot.security.IAuthenticatedUserProvider;
import org.jsets.fastboot.util.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;

/**
 * 
 * StatementHandler 拦截器
 * 
 * @author wangjie
 *
 */
@Slf4j
@Intercepts({ 
	@Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class }),
	@Signature(type = StatementHandler.class, method = "update", args = { Statement.class }) 
})
public class StatementHandlerInterceptor implements Interceptor {

	private static final CCJSqlParserManager parser = new CCJSqlParserManager();

	private JaversService javersService;
	private IDataScopeService dataScopeService;
	private IAuthenticatedUserProvider authenticatedUserProvider;

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		if (InterceptorUtils.isPrepareInvocation(invocation)) {
			MetaObject statementHandlerMetaObject = SystemMetaObject.forObject(statementHandler);
			MappedStatement mappedStatement = (MappedStatement) statementHandlerMetaObject.getValue("delegate.mappedStatement");
			if (InterceptorUtils.isSelectSqlCommand(mappedStatement)) {
				String mappedId = mappedStatement.getId();
				DataScopeOption option = PersistentAnnotationResolver.getDataScope(mappedId);
				if (Objects.nonNull(option)) {
					String sql = statementHandler.getBoundSql().getSql();
					StringReader stringReader = new StringReader(sql);
					Select select = (Select) parser.parse(stringReader);
					PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
					this.getDataScopeService().sqlRender(option, plainSelect, invocation.getArgs());
					stringReader.close();
					statementHandlerMetaObject.setValue("delegate.boundSql.sql", plainSelect.toString());
				}
			}
		}

		if (InterceptorUtils.isUpdataInvocation(invocation)) {
			Object object = statementHandler.getParameterHandler().getParameterObject();
			Object entity = InterceptorUtils.getEntity(object);
			if(Objects.nonNull(entity)) {
				PersistentOption option = PersistentAnnotationResolver.getOption(entity.getClass());
				if (Objects.nonNull(option)) {
					if (option.isAuditable()) {// 审计快照
						log.info("保存审计快照[{}]", object.getClass().getName());
						this.getJaversService().getJavers().commit(this.getAuthenticatedUserProvider().getCurrentUsernameIfLogined(), entity);
					}
				}
			}
		}

		return invocation.proceed();
	}

	private IAuthenticatedUserProvider getAuthenticatedUserProvider() {
		if (null != this.authenticatedUserProvider) {
			return this.authenticatedUserProvider;
		}

		synchronized (this) {
			if (null != this.authenticatedUserProvider) {
				return this.authenticatedUserProvider;
			}
			this.authenticatedUserProvider = SpringContextHolder.getBean(IAuthenticatedUserProvider.class);
			return this.authenticatedUserProvider;
		}
	}
	
	private IDataScopeService getDataScopeService() {
		if (null != this.dataScopeService) {
			return this.dataScopeService;
		}

		synchronized (this) {
			if (null != this.dataScopeService) {
				return this.dataScopeService;
			}
			this.dataScopeService = SpringContextHolder.getBean(IDataScopeService.class);
			return this.dataScopeService;
		}
	}

	private JaversService getJaversService() {
		if (null != this.javersService) {
			return this.javersService;
		}

		synchronized (this) {
			if (null != this.javersService) {
				return this.javersService;
			}
			this.javersService = SpringContextHolder.getBean(JaversService.class);
			return this.javersService;
		}
	}

}